import mjs.processing.mobile.mrest.*;
import mjs.processing.mobile.mvideo.*;
import processing.phone.*;

/**
 * MFlickrUploader 
 * 
 * Allow upload images to Flickr
 *
 * To acces the Flickr services you must obtain an API Key you can visit the 
 * site http://www.flickr.com/services/api/keys/ and copy the key to the the 
 * file data/apikey.txt
 *
 * This sketch use the Flickr Rest API to authenticate the user and upload the
 * photo using a post call.
 *
 * When the sketch shows an authentication error or the message 
 * "Needs Authentication", the user must load the page 
 * 	http://www.flickr.com/auth-<AuthNumber>
 * and allow the application to acces to the account, then set the 
 * mini token pressing the 2 key and authenticating the user pressing key 1 
 *
 * The conf.txt file contains 
 *  - The API Key 
 *  - The Secret 
 *  - The Auth Number
 *
 * @author Mary Jane Soft - Marlon J. Manrique
 * @version 0.1 (2008,04,08)
 *
 * $Id: MFlickrUploader.pde 378 2008-04-07 21:02:28Z marlonj $
 */

// Flickr API values 
String apiKey;  // The application Key 
String secret;  // The application Secret 
String miniToken = ""; // The authentication token for the user
String token;  // The authentication token 
String serverURL = "http://api.flickr.com/services/rest/";
String serviceID;

// Flickr Upload values 
String uploadPath = "/services/upload/";
String[] params = { "api_key", "auth_token", "api_sig", "photo" }; 
Object[] values = { null, null, null, null };
PClient uploadClient = new PClient(this,"api.flickr.com");
PRequest uploadRequest;

// Rest authentication on Flickr
MRestRequest mRequest;
MRestResponse mResponse;

// The capture image 
byte[] imgData;
PImage snapshot;
MCapture mCapture;

// Phone Library
Phone phone;

// Message to show
String header = "MFlickrUploader";
String message = "";

/*
 * Init properties
 */
void setup()
{
  // Don't Loop 
  noLoop();
  
  // Set font properties
  textAlign(CENTER);
  textFont(loadFont());    

  // Load data from phone 
  loadConf();
  loadToken();
  
  // Create player and set key 
  createCapturePlayer();  
  softkey("Capture");
  
  // Create phone library 
  phone = new Phone(this);

  // Debug message 
  println("Setup Done");
}

/*
 * Draw image, information and video
 */
void draw()
{
  // Clear the screen
  background(0);
  
  // Draw the image if available at center 
  if(snapshot != null)
    image(snapshot,(width-snapshot.width)/2,(height-snapshot.height)/2);

  // Draw text 
  text(header,width/2,20);    
  text(message,width/2,height-20);
}

/*
 * Check for user actions 
 *
 * @param label Sofkey label 
 */
void softkeyPressed(String label)
{    
  // Capture or Upload image 
  if(label.equals("Capture"))
    captureImage();
  else if(label.equals("Upload"))
    uploadImage();          
}

/*
 * Check for key events 
 */
void keyPressed()
{
  // Check for actions 
  switch(keyCode)
  {
    case '1' : authenticate(); break;
    case '2' : askForMiniToken(); break;
    case '3' : showAuthPage(); break;
  }
}

/*
 * Check library events 
 *
 * @param library The library that fires the events 
 * @param event The event type 
 * @param data The data send 
 */
void libraryEvent(Object library, int event, Object data)
{
  println(library + " " + event + " " + data);
  
  // Check the upload process
  if(library == uploadRequest)
  {
    switch(event)
    {
      // If connection is available, read response 
      case PRequest.EVENT_CONNECTED : 
        uploadRequest.readBytes(); 
        break;

      // If upload is done, check response and clear the image
      case PRequest.EVENT_DONE : 
        checkUploadResponse(data);
        break;        

      // If an error, show at screen 
      case PRequest.EVENT_ERROR : 
        message = "Error " + data;
        mCapture.show();
        break;        
    }
  }
  // If authentication 
  else if(library == mRequest)
  {
    switch(event)
    {
      // If authentication is done, get the token and save it 
      case MRestRequest.EVENT_DONE :
        checkAuthentication((MRestResponse) data);
        break;

      // If error, show at screen 
      case MRestRequest.EVENT_ERROR : 
        message = "Error " + data;
        break;        
    }
  }
  
  // Update content of the screen 
  redraw();
}

/*
 * Load configuration, apikey and secret 
 */
void loadConf()
{
  // Get the configuration 
  String[] array = loadStrings("conf.txt");

  // If not file or key
  if(array.length == 0)
    message = "No API Key found";
  else
  {
    apiKey = array[0];
    secret = array[1];
    serviceID = array[2];
  }  
}

/*
 * Create the video player at center of the screen 
 */
void createCapturePlayer()
{
  // Create capture object 
  mCapture = new MCapture(this);

  // Calculate capture position
  int videoWidth = mCapture.width() / 2;
  int videoHeight = mCapture.height() / 2;
  int videoX = (width - videoWidth) / 2;
  int videoY = (height - videoHeight) / 2;
  
  // Set location and size 
  mCapture.location(videoX,videoY);
  mCapture.size(videoWidth,videoHeight);
}

/*
 * Authenticate the user in Flickr
 */
void authenticate()
{
  // Show message 
  message = "User authentication ...";
  redraw();
  
  // Create signature 
  String sig = createSignature("api_key" + apiKey 
    + "methodflickr.auth.getFullToken" 
    + "mini_token" + miniToken);
  
  // Create authentication request 
  mRequest = new MRestRequest(this,serverURL);
  mRequest.parameter("api_key",apiKey);
  mRequest.parameter("method","flickr.auth.getFullToken");
  mRequest.parameter("mini_token",miniToken);
  mRequest.parameter("api_sig",sig);  

  // Send Request 
  mRequest.send();
}

/*
 * Save token to use later 
 */
void saveToken()
{
  // Create and array with the token value and save into RMS
  String[] array = { token };
  saveStrings("token.txt",array);  
}

/*
 * Load the token from the RMS
 */
void loadToken()
{
  // Load the token 
  String[] array = loadStrings("token.txt");
  
  // Check if the token is available 
  if(length(array) == 1)
  {
    token = array[0];
    message = "Ready";
  }
  else
    message = "Needs Authentication";
}

/*
 * Capture the image 
 */
void captureImage()
{
  // Capture the image data 
  imgData = mCapture.read();
  
  // Hide the player 
  mCapture.hide();
  
  // Convert the data to image 
  snapshot = new PImage(imgData);

  // Resize image 
  PImage thumbnail = new PImage(mCapture.width(),mCapture.height());
  thumbnail.copy(snapshot,0,0,snapshot.width,snapshot.height,
    0,0,mCapture.width(),mCapture.height());

  // Set snapshot like the thumbnail 
  snapshot = thumbnail;
  
  // Change the softkey 
  softkey("Upload");

  // Update content of the screen 
  redraw();
}

/*
 * Upload the image to Flickr
 */
void uploadImage()
{
  // Change message 
  message = "Uploading image ...";
  
  // Create signatura 
  String sig = createSignature("api_key" + apiKey 
    + "auth_token" + token);
    
  // Update upload parameters 
  values[0] = apiKey;
  values[1] = token;
  values[2] = sig;
  values[3] = imgData;
    
  // Send HTTP post request 
  uploadRequest = uploadClient.POST(uploadPath,params,values);

  // Update content of the screen 
  redraw();
}

/*
 * Create the signature for the parameters 
 * 
 * @param arguments The arguments to send to Flickr
 * @return The md5 signature for the parameters
 */
String createSignature(String arguments)
{
  // Add the secret 
  String dataStr = secret + arguments;
  
  // Get the parameters like bytes 
  byte[] data = dataStr.getBytes();
  
  // Create a MD% generatos and calculate 
  MD5 md5 = new MD5(data);
  data = md5.doFinal();
  
  // Return a hexadecimal string with the value
  return md5.toHex(data);
}

/*
 * Checks the authentication response 
 *
 * @param mResponse The auth response 
 */
void checkAuthentication(MRestResponse mResponse)
{
  // Print response 
  println("Response: " + mResponse);
  
  // Get stat response 
  String stat = mResponse.get("/rsp/@stat");
  
  //  If ok, get the token and save it 
  if(stat.equals("ok"))
  {
    token = mResponse.get("/rsp/auth/token/text()");
    saveToken();
    message = "Authetication Done";
  }
  // Show error 
  else
    message = "Error : " + mResponse.get("/rsp/err/@msg");  
  
  // Refresh data  
  redraw();
}

/*
 * Ask for minitoken to the user 
 */
void askForMiniToken()
{
  // Show form to add minitoken 
  String newMiniToken = textInput("MiniToken",miniToken,12);
  
  // If not cancel update miniToken
  if(newMiniToken != null)
    miniToken = newMiniToken;
  
  // Clear token 
  token = null;
}

/*
 * Show authentication page 
 */
void showAuthPage()
{
  // Create the url 
  String authURL = "http://www.flickr.com/auth-" + serviceID;
  
  // Open the url in the phone browser 
  phone.launch(authURL);
}

/* 
 * Check Upload Response 
 *
 * @param data The respponse
 */
void checkUploadResponse(Object data)
{
  // Get response like a string 
  String response = new String((byte[]) data);
  
  // Check if fails 
  if(response.indexOf("fail") > 0)
  {
    // Get the fail message 
    int index = response.indexOf("msg=");
    int nextIndex = response.indexOf("\"",index+5);
    
    // Get Message 
    message = response.substring(index+5,nextIndex);
  }
  else
  {
    // Show message 
    message = "Upload Done";
    
    // Get the photoid message 
    int index = response.indexOf("<photoid>");
    int nextIndex = response.indexOf("</photoid>");
    
    // Get Message 
    message = "PhotoID :" + response.substring(index+9,nextIndex);    
    
    // Update status 
    softkey("Capture");
    snapshot = null;
    mCapture.show();
  }
}
